/*
  提示：
  ws2812 8*8硬屏
  开发板选择 NodeMCU 1.0

*/
#include <FastLED.h>
#include "OneButton.h"
#include <Adafruit_NeoPixel.h>
#include <Adafruit_GFX.h>
#include <Adafruit_NeoMatrix.h>
#include <ESP8266WiFi.h>
#include <WiFiManager.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <arduinoFFT.h>
#include "GitFans.h"
//wifi设置 可不设置
#ifndef STASSID
#define STASSID "wifi_name"//wifi名字
#define STAPSK  "12345678"//wifi密码
#endif
#define PIN              2       //ws2812 DAT 接的引脚编号，注意开发板不同，=====请更改=====
#define buttonPin   4       //D2 四脚按键一端接此引脚 一端接地   按键 按下松开后切换
#define NUMPIXELS      64                           ////ws2812 灯数  =====请更改=====
int   maxBrightness = 250;
#include <NTPClient.h>

#define SAMPLES 64        // Must be a power of 2
#define MIC_IN A0         // Use D0 for mic input
#define xres 8            // Total number of  columns in the display
#define yres 8            // Total number of  rows in the display

Adafruit_NeoPixel pad = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);//创建对象，色序号GRB
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(8, 8, 1, 1, PIN,
                            NEO_MATRIX_TOP  + NEO_TILE_LEFT  + NEO_MATRIX_COLUMNS   + NEO_MATRIX_PROGRESSIVE +
                            NEO_MATRIX_TOP + NEO_MATRIX_RIGHT + NEO_MATRIX_ROWS + NEO_MATRIX_PROGRESSIVE,
                            NEO_GRB + NEO_KHZ800);
//CRGB leds[NUMPIXELS];
/*
  const uint16_t colors[] = {
  matrix.Color(255, 0, 0), matrix.Color(0, 255, 0), matrix.Color(0, 0, 255), matrix.Color(0, 100, 255), matrix.Color(100, 0, 255)
  };

*/

double vReal[SAMPLES];
double vImag[SAMPLES];
int freq_gain2[xres] = {40, 42, 45, 47, 51, 55, 57, 59};
int Intensity[xres] = { }; // initialize Frequency Intensity to zero
int Displacement = 1;          // Create LED Object
arduinoFFT FFT = arduinoFFT();  // Create FFT object

int x = matrix.width();
int pass = 0;
int Mode = 1;
uint8_t color_index = 0;
int scroll_limit = 30; // each text character is 6 pixels wide
char incomingPacket[1024]; //buffer to hold incoming packet,
String  H;
String  M;
String timeText = "Time";
String UID = "4772302";    //bilibili UID

String FansURL;
GitFans gitfans;

String bitdata[10]={"0111010101010111","0010011000100111","0111000100100111","0111000100110101","0101010101110001","0111010000010111","0010010001110101","0111000100100010","0111010101110101","0111010101110001"};

String bitdata8[10]={
  "00000111010101010101010101010111","00000010011000100010001000100111",
  "00000111010100010010001001000111","00000111010100010011000101010111",
  "00000101010101010111000100010001","00000111010001000111000100010111",
  "00000111010101000111010101010111","00000111000100010010001001000100",
  "00000111010101010111010101010111","00000111010101010111000101010111"
};

String bitdata20[13]={
  "01110101010101010111","00100110001000100111",
  "01110001011101000111","01110001011100010111",
  "01010101011100010001","01110100011100010111",
  "01110100011101010111","01110001000100010001",
  "01110101011101010111","01110101011100010111",
  "01010101011101010101","01110010001000100010",
  "01110010011100010011"
};

WiFiUDP udp;
NTPClient timeClient(udp, "ntp1.aliyun.com", 60 * 60 * 8, 30 * 60 * 1000);
OneButton button(buttonPin, true);
void setup() {
  Serial.begin(115200);
  WiFiManager wifiManager;
  wifiManager.autoConnect("MatrixTime");
  // 如果您希望该WiFi添加密码，可以使用以下语句：
  // wifiManager.autoConnect("AutoConnectAP", "12345678");
  // 以上语句中的12345678是连接AutoConnectAP的密码

  // WiFi连接成功后将通过串口监视器输出连接成功信息
  Serial.println("");
  Serial.print("ESP8266 Connected to ");
  Serial.println(WiFi.SSID());              // WiFi名称
  Serial.print("IP address:\t");
  Serial.println(WiFi.localIP());           // IP
  button.attachClick(colorChange);
  button.attachDoubleClick(modeChange);
  // FastLED.addLeds<WS2812, PIN, GRB>(leds, NUMPIXELS).setCorrection(TypicalLEDStrip);
  matrix.begin();

  matrix.setTextWrap(false);
  matrix.setBrightness(maxBrightness);
  //matrix.setTextColor(colors[0]);
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(500);
  }
  Serial.print("Connected! IP address: ");
  Serial.println(WiFi.localIP());
  pad.begin();
  pad.setBrightness(50);
  FansURL = gitfans.GitURL(UID);    //获得API
}


void modeChange()
{
  if (++Mode > 3)Mode = 0;//模式变化
  Serial.println("按键切换模式");
  Serial.println(Mode);

  pad.clear();//每次模式变化的时候先灭掉所有灯
  pad.show();
  matrix.clear();
  matrix.show();
  switchMode(true);

}

void colorChange(){
  if (++color_index > 13)color_index = 0;//模式变化
  Serial.println("按键切换颜色");
  Serial.println(color_index);
  switchMode(true);
}


//Matrix
int pos[8] = {random(0, 16), random(0, 16), random(0, 16), random(0, 16), random(0, 16), random(0, 16), random(0, 16), random(0, 16)};

//RGB颜色数组 颜色根据自己喜好调整，我太难了https://www.sojson.com/rgb.html
uint32_t myRGBColorPalette[16] = { pad.Color(0, 250, 250, 250),  pad.Color(0, 210, 0, 230),  pad.Color(0, 210, 0, 200),  pad.Color(0, 150, 0, 150),
                                   pad.Color(0, 150, 50, 100),  pad.Color(0, 120, 0, 80),  pad.Color(0, 50, 0, 50),  pad.Color(0, 20, 10, 20),
                                   pad.Color(0, 250, 250, 250),  pad.Color(0, 210, 0, 230),  pad.Color(0, 210, 0, 200),  pad.Color(0, 150, 0, 150),
                                   pad.Color(0, 150, 50, 100),  pad.Color(0, 120, 0, 80),  pad.Color(0, 50, 0, 50),  pad.Color(0, 20, 10, 20)
                                 };

//RGB颜色数组https://www.sojson.com/rgb.html
uint16_t myRGBColor[14] = { matrix.Color(30,144,255), matrix.Color(135,206,250), matrix.Color(0, 0, 205),  matrix.Color(150, 0, 150),
                                     matrix.Color(255,255,255),  matrix.Color(255,240,245),  matrix.Color(255,193,193),matrix.Color(255,106,106),
                                     matrix.Color(255,165,0),  matrix.Color(255,64,64),  matrix.Color(255,255,0),  matrix.Color( 46,139,87),
                                   matrix.Color(255,0,255),  matrix.Color(0,250,154)
                                 };


void Matrix()
{
  for (int y = 0; y < 8; y++)
  {
    for (int x = 0; x < 8; x++)
    {
      int P = pos[x] + y;
      if (P > 15)P = P - 16;
      pad.setPixelColor(x * 8 + y, myRGBColorPalette[P]);
//      if (x % 2 == 0) pad.setPixelColor(x * 8 + y, myRGBColorPalette[P]);
//      if (x % 2 != 0)pad.setPixelColor((x + 1) * 8 - y - 1, myRGBColorPalette[P]);
    }
  }
  for (int c = 0; c < 8; c++)
  {
    if (--pos[c] < 0)pos[c] = 15;
  }
  pad.show();
  delay(100);
}


void drawFastXLine(int16_t x, int16_t y, int16_t h, uint16_t color){
  for(int i=x;i<x+h;i++){
    matrix.drawPixel(i,y,color);
  }
}

void drawFastYLine(int16_t x, int16_t y, int16_t h, uint16_t color){
  for(int i=y;i<y+h;i++){
    matrix.drawPixel(x,i,color);
  }
}

unsigned long lastTime;
void showTime(bool implement)
{
  timeClient.update();
  H = timeClient.getFormattedTime().substring(0, 2);
  M = timeClient.getFormattedTime().substring(3, 5);
  timeText = H + ":" + M;
  if ( millis() - lastTime > 500 | implement)
  {
    lastTime = millis();
    if(color_index>0){
      showHours(myRGBColor[color_index-1]);
      showbittime(M,3,myRGBColor[color_index]);
    }else{
      showHours(pad.Color(0, 210, 0, 230));
      showbittime(M,3,pad.Color(0, 20, 10, 20));
    }
    matrix.show();
  }
}

void showHours(uint16_t colorxy){
  int hours = timeClient.getHours();
  if(hours>12){
    hours = hours-12;
  }
  drawFastXLine(0,0,8,pad.Color(0, 0, 0, 0));
  drawFastXLine(0,1,8,pad.Color(0, 0, 0, 0));
  if(hours<7){
    drawFastXLine(1,0,hours,colorxy);
  }else{
    drawFastXLine(1,0,6,colorxy);
    drawFastXLine(1,1,hours-6,colorxy);
  }
}

void showFs(bool implement){
  if ( millis() - lastTime > 20000 | implement)
  {
    lastTime = millis();

    gitfans.ParseFans(FansURL);    //处理粉丝数据
    Serial.println(Fansnumber);    //打印粉丝数
    if(Fansnumber<100000){
      //showbitnumber(String(Fansnumber),4,8,0,0,pad.Color(0,178,34,34));
      if(color_index>0){
        showbitnumber(String(Fansnumber),4,5,0,2,myRGBColor[color_index]);
      }else{
        showbitnumber(String(Fansnumber),4,5,0,2,pad.Color(0,178,34,34));
      }

    }else{
      //showbitnumber("99",4,8,0,0,pad.Color(0,255,215,0));
      showbitnumber("99",4,5,0,2,pad.Color(0,255,215,0));
    }
    matrix.show();
  }
}

void showbittime(String nowtime, int y, uint16_t colorxy){
  if(nowtime.toInt()<10){
    //showbitmap(bitdata[(int)(0)], 0, y, colorxy);
    //showbitmap(bitdata[(int)((String(nowtime.charAt(1)).toInt() + 1) - 1)], 4, y, colorxy);
    showbitmap8(bitdata20[(int)(0)],4,5, 0, y, colorxy);
    showbitmap8(bitdata20[(int)((String(nowtime.charAt(1)).toInt() + 1) - 1)], 4, 5, 4, y, colorxy);
  }else{
    //showbitmap(bitdata[(int)((String(nowtime.charAt(0)).toInt() + 1) - 1)], 0, y, colorxy);
    //showbitmap(bitdata[(int)((String(nowtime.charAt(1)).toInt() + 1) - 1)], 4, y, colorxy);
    showbitmap8(bitdata20[(int)((String(nowtime.charAt(0)).toInt() + 1) - 1)],4,5, 0, y, colorxy);
    showbitmap8(bitdata20[(int)((String(nowtime.charAt(1)).toInt() + 1) - 1)],4,5, 4, y, colorxy);
  }
 }

void showbitnumber(String number, int xlength, int ylength, int x, int y, uint16_t colorxy){
  if(number.toInt()<10){
    //showbitmap8(bitdata8[(int)(0)],xlength,ylength, 0, y, colorxy);
    //showbitmap8(bitdata8[(int)((String(number.charAt(1)).toInt() + 1) - 1)],xlength,ylength, 4, y, colorxy);
    showbitmap8(bitdata20[(int)(0)],xlength,ylength, 0, y, colorxy);
    showbitmap8(bitdata20[(int)((String(number.charAt(1)).toInt() + 1) - 1)],xlength,ylength, 4, y, colorxy);
  }else if(number.toInt()<100){
    //showbitmap8(bitdata8[(int)((String(number.charAt(0)).toInt() + 1) - 1)],xlength,ylength, 0, y, colorxy);
    //showbitmap8(bitdata8[(int)((String(number.charAt(1)).toInt() + 1) - 1)],xlength,ylength, 4, y, colorxy);
    showbitmap8(bitdata20[(int)((String(number.charAt(0)).toInt() + 1) - 1)],xlength,ylength, 0, y, colorxy);
    showbitmap8(bitdata20[(int)((String(number.charAt(1)).toInt() + 1) - 1)],xlength,ylength, 4, y, colorxy);
  }else if(number.toInt()<1000){
    showbitmap8(bitdata20[(int)((String(number.charAt(0)).toInt() + 1) - 1)],xlength,ylength, 0, y, colorxy);
    showbitmap8(bitdata20[10],xlength,ylength, 4, y, colorxy);
  }else if(number.toInt()<10000){
    showbitmap8(bitdata20[(int)((String(number.charAt(0)).toInt() + 1) - 1)],xlength,ylength, 0, y, colorxy);
    showbitmap8(bitdata20[11],xlength,ylength, 4, y, colorxy);
  }else if(number.toInt()<100000){
    showbitmap8(bitdata20[(int)((String(number.charAt(0)).toInt() + 1) - 1)],xlength,ylength, 0, y, colorxy);
    showbitmap8(bitdata20[12],xlength,ylength, 4, y, colorxy);
  }else{
    showbitmap8(bitdata20[(int)((String(number.charAt(0)).toInt() + 1) - 1)],xlength,ylength, 0, y, colorxy);
    showbitmap8(bitdata20[(int)((String(number.charAt(1)).toInt() + 1) - 1)],xlength,ylength, 4, y, colorxy);
  }
}

void showbitmap(String bitrgbstr, int x, int y, uint16_t colorxy) {
  for (int i = x; i < x+(4); i = i + (1)) {
    for(int j = y; j < y+(4); j = j + (1)){
      if (String(bitrgbstr.charAt(((j-y)*4+i-x))).toInt() != 0) {
        matrix.drawPixel(i,j,colorxy);
      } else {
        matrix.drawPixel(i,j,pad.Color(0, 0, 0, 0));
      }
    }
  }
}

void showbitmap8(String bitrgbstr, int xlength, int ylength, int x, int y, uint16_t colorxy) {
  //Serial.println("bitrgbstr = " + bitrgbstr);
  for (int i = x; i < x+(xlength); i = i + (1)) {
    for(int j = y; j < y+(ylength); j = j + (1)){
      if (String(bitrgbstr.charAt(((j-y)*xlength+i-x))).toInt() != 0) {
        matrix.drawPixel(i,j,colorxy);
      } else {
        matrix.drawPixel(i,j,pad.Color(0, 0, 0, 0));
      }
    }
  }
}

void showFFT() {
  //Collect Samples
    getSamples();
    //Update Display
    displayUpdate();
    matrix.show();
    delay(1);
}

void getSamples(){
  for(int i = 0; i < SAMPLES; i++){
    vReal[i] = analogRead(MIC_IN);
    Serial.println(vReal[i]);
    vImag[i] = 0;
  }
  //FFT
  FFT.Windowing(vReal, 1, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
  FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
  FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);
  //Update Intensity Array
  for(int i = 2; i < (xres*Displacement)+2; i+=Displacement){
    vReal[i] = constrain(vReal[i],freq_gain2[i-2] ,1300);            // set max value for input data
    vReal[i] = map(vReal[i], freq_gain2[i-2], 1200, 0, yres);        // map data to fit our display
    Intensity[(i/Displacement)-2] --;                      // Decrease displayed value
    if (vReal[i] > Intensity[(i/Displacement)-2])          // Match displayed value to measured value
      Intensity[(i/Displacement)-2] = vReal[i];
  }
}


void displayUpdate(){
  int color = 0;
  for(int i = 0; i < xres; i++){
    CRGB c_color = CHSV(color, 255, 255);
    if(color_index>0){
      drawFastYLine(i,yres-Intensity[i],Intensity[i],myRGBColor[color_index]);
    }else{
      drawFastYLine(i,yres-Intensity[i],Intensity[i],matrix.Color(c_color.r,c_color.g,c_color.b));
    }
    drawFastYLine(i,0,yres-1-Intensity[i],matrix.Color(0,0,0));

    color += 255/xres;             // Increment the Hue to get the Rainbow
    if(color>255){
      color = 0;
    }
  }
}

void switchMode(bool change){
  switch (Mode)
  {
    case 0:
      Matrix();
      break;
    case 1:
      showTime(change);
      break;
    case 2:
      showFFT();
      break;
    case 3:
      showFs(change);
      break;
  }
}

void loop()
{
  button.tick();

  //timeText = "I LOVE YOU";
  switchMode(false);
}
